/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "FusionStorageCleanFile.h"
#include <sys/file.h>
#include <fstream>
#include <system/System.hpp>
#include <unordered_set>
#include "log/Log.h"
#include "security/cmd/CmdParam.h"
#include "security/cmd/CmdExecutor.h"
#include "common/utils/Utils.h"

namespace {
const std::string MODULE_NAME = "FusionStorageCleanFile";
const std::string CLEAN_FILE_NAME = VirtPlugin::VIRT_PLUGIN_PATH + "conf/clean.ini";
const std::string STATUS_BUZY = "busy";
const std::string STATUS_IDLE = "idle";

const int SAVE_TO_FILE_MAXTIME = 10000;
}  // namespace

VIRT_PLUGIN_NAMESPACE_BEGIN

GuardFileLock::GuardFileLock(const std::string &filePath) : m_fd(-1)
{
    m_fd = open(filePath.c_str(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (m_fd == -1) {
        ERRLOG("Open file[%s] failed.", filePath.c_str());
    }
    (void)chmod(filePath.c_str(), S_IRUSR | S_IWUSR);

    if (flock(m_fd, LOCK_EX) == -1) {
        ERRLOG("Lock file[%s] failed.", filePath.c_str());
    }
}

GuardFileLock::~GuardFileLock()
{
    if (m_fd == -1) {
        ERRLOG("Lock file[%d] is invalid.", m_fd);
        return;
    }
    (void)flock(m_fd, LOCK_UN);
    (void)close(m_fd);
}

FusionStorageCleanFile::FusionStorageCleanFile()
    : m_cleanFilePath(Module::EnvVarManager::GetInstance()->GetAgentHomePath() + CLEAN_FILE_NAME)
{}

FusionStorageCleanFile::~FusionStorageCleanFile()
{}

std::string FusionStorageCleanFile::TrimRight(const std::string &str)
{
    std::string strTemp = str;
    for (std::string::reverse_iterator rIt = strTemp.rbegin(); rIt != strTemp.rend();) {
        int i = 0;
        if (' ' == (*rIt) || '\r' == (*rIt) || '\n' == (*rIt)) {
            std::string::iterator it = (++rIt).base();
            rIt = std::string::reverse_iterator(strTemp.erase(it));
        } else {
            ++rIt;
        }
    }

    return strTemp;
}

int32_t FusionStorageCleanFile::AddOrReplaceItemNoLock(const std::string &key, const std::string &item, const bool busy)
{
    std::string status = busy ? STATUS_BUZY : STATUS_IDLE;
    std::vector<std::string> items;
    int ret = GetAllItemsNoLock(items);
    if (ret != SUCCESS) {
        ERRLOG("Get all items failed.");
        return ret;
    }

    // 获取时间戳
    uint64_t curTimeStamp =
        std::chrono::system_clock::now().time_since_epoch().count() / std::chrono::system_clock::period::den;

    std::vector<std::string>::iterator itr;
    for (itr = items.begin(); itr != items.end(); ++itr) {
        std::string::size_type pos = itr->find(key);
        if (pos == 0) {
            *itr = key + "|" + TrimRight(item) + "|" + status + "|" + std::to_string(curTimeStamp);
            break;
        }
    }

    if (itr == items.end()) {
        items.push_back(key + "|" + TrimRight(item) + "|" + status + "|" + std::to_string(curTimeStamp));
    }

    return SaveToFile(items);
}

int32_t FusionStorageCleanFile::AddOrReplaceItem(const std::string &key, const std::string &item, const bool busy)
{
    GuardFileLock lock(m_cleanFilePath);

    return AddOrReplaceItemNoLock(key, item, busy);
}

int32_t FusionStorageCleanFile::GetAllItems(std::vector<std::string> &items)
{
    GuardFileLock lock(m_cleanFilePath);

    return GetAllItemsNoLock(items);
}

int32_t FusionStorageCleanFile::RemoveItemNoLock(const std::string &key)
{
    std::vector<std::string> items;
    int ret = GetAllItemsNoLock(items);
    if (ret != SUCCESS) {
        ERRLOG("Get all items failed.");
        return ret;
    }

    std::vector<std::string> newItems;
    for (std::vector<std::string>::iterator itr = items.begin(); itr != items.end(); ++itr) {
        std::string::size_type pos = itr->find(key);
        if (pos != 0) {
            newItems.push_back(*itr);
        }
    }
    return SaveToFile(newItems);
}

int32_t FusionStorageCleanFile::RemoveItem(const std::string &key)
{
    GuardFileLock lock(m_cleanFilePath);

    return RemoveItemNoLock(key);
}

void FusionStorageCleanFile::SetAllItemIdle()
{
    std::unordered_set<std::string> pathWhitelist;
    pathWhitelist.insert(m_cleanFilePath);
    std::vector<Module::CmdParam> cmdParam {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sed"),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-i"),
        Module::CmdParam(Module::COMMON_PARAM, "\"s/|busy/|idle/g\""),
        Module::CmdParam(Module::PATH_PARAM, m_cleanFilePath)
    };
    std::vector<std::string> cmdOut;
    std::string cmdStr;
    CmdResult returnValue;
    Utils::TransCmdParamsToCmdStr(cmdParam, cmdStr);
    SecurityService::RunCommand(returnValue, cmdStr);
    cmdOut = std::move(returnValue.output);
    if (returnValue.result != 0) {
        WARNLOG("Failed to set clean item to busy");
    }
}

int32_t FusionStorageCleanFile::SaveToFile(const std::vector<std::string> &items)
{
    std::ofstream fin(m_cleanFilePath.c_str(), std::ios::trunc);
    if (!fin) {
        WARNLOG("File no exist, create it, file is: %s.", m_cleanFilePath.c_str());
        return FAILED;
    }

    long itemNum = 0;
    for (std::vector<std::string>::const_iterator itr = items.begin(); itr != items.end(); ++itr) {
        /* max item = 1000,if exceed ,skip */
        if (itemNum++ >= SAVE_TO_FILE_MAXTIME) {
            WARNLOG("Clean file size too big.");
            continue;
        }
        fin << *itr << std::endl;
    }
    fin.close();
    return SUCCESS;
}

int32_t FusionStorageCleanFile::GetAllItemsNoLock(std::vector<std::string> &items)
{
    std::ifstream fout(m_cleanFilePath.c_str());
    if (!fout) {
        WARNLOG("There in no clean info.");
        return FAILED;
    }

    while (!fout.eof()) {
        std::string tmp = "";
        getline(fout, tmp);
        if (tmp.empty()) {
            continue;
        }

        items.push_back(tmp);
    }
    fout.close();

    return SUCCESS;
}

void FusionStorageCleanFile::RemoveSpecificArrayElement(
    Json::Value &itemJson, const std::string &key, const std::string &value)
{
    Json::Value obj;
    for (uint32_t i = 0; i < itemJson[key].size(); i++) {
        if (!itemJson[key][i].isNull() && itemJson[key][i].isString() &&
            itemJson[key][i].asString() != value) {
            obj.append(itemJson[key][i]);
        } else if (!itemJson[key][i].isNull() && itemJson[key][i].isObject() &&
                   itemJson[key][i]["key"].asString() != value) {
            obj.append(itemJson[key][i]);
        }
    }

    Json::Value removed;
    itemJson.removeMember(key, &removed);
    itemJson[key] = obj;

    AddOrReplaceItem(itemJson["cache_uuid"].asString(), Module::CJsonUtils::ConvertJsonToString(itemJson), true);
}

VIRT_PLUGIN_NAMESPACE_END
